#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>
#include <linux/printk.h>

/**
 * example: SH_DEBUG(D_ERR, "major=%d", devno);
 */
#define __PT_DEBUG__
#ifdef __PT_DEBUG__
#define JZ_DEBUG(format,...) \
do {\
    printk(KERN_ERR "INFO: "__FILE__" %s->%d, "format"\n", __func__, __LINE__, ##__VA_ARGS__);\
    pr_info(format,##__VA_ARGS__);\
} while(0)
#else
#define JZ_DEBUG(level,format,...) \
do {\
} while(0)
#endif


#define DTSLED_CNT      1
#define DTSLED_NAME     "myled"

/*  这个几个类型由于s3c2440内核没有提供给client device driver使用,只能自己定义 */
struct pinctrl_setting_mux {
        unsigned group;
        unsigned func;
};

struct pinctrl_setting_configs {
        unsigned group_or_pin;
        unsigned long *configs;
        unsigned num_configs;
};

struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;
    struct pinctrl_dev *pctldev;
    const char *dev_name;
    union {
        struct pinctrl_setting_mux mux;   // mux配置数据
        struct pinctrl_setting_configs configs;  // config配置数据
    } data;
};

struct pinctrl_state {
        struct list_head node;
        const char *name;
        struct list_head settings;
};

/* 定义led结构体 */
struct led_dev {
    dev_t devid;                   /* 字符设备编号 */
    struct cdev cdev;              /* 保存操作结构体的字符设备 */
    struct class *class;           /* class */
    struct device *device;         /* 设备类 */
    struct device_node *nd;        /* 设备节点 */
    struct pinctrl *pinctrl;       /* pin control state holder */
    struct pinctrl_state *state;   /* 当前pin control state */
};

static struct led_dev myled = {0};

static int led_open(struct inode *inode, struct file *file)
{
    dev_err(NULL, "name=%s\n", file->f_path.dentry->d_name.name);

    return 0;
}

/* 点亮/熄灭 LED01 */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int val = 0,ret = 0;

    ret = copy_from_user(&val, buf, count);   // 用户空间到内核空间传递数据

    printk("value %d",val);

    if(val == 1){
        /* 点亮 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
        if (IS_ERR(myled.state)) {
            dev_dbg(myled.device, "no default pinctrl state\n");
            ret = PTR_ERR(myled.state);
            return ret;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return ret;
        }else{
            printk("pinctrl select state success\n");
        }
    }
    else{
        /* 熄灭 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
        if (IS_ERR(myled.state)) {
            dev_dbg(myled.device, "no myled_off pinctrl state\n");
            ret = PTR_ERR(myled.state);
            return ret;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return ret;
        }else{
            printk("pinctrl select state success\n");
        }
    }

    return 0;
}

static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,
    .open   =   led_open,
    .write  =   led_write,
};

/*
 * 当驱动和硬件信息匹配成功之后，就会调用probe函数，驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int led_probe(struct platform_device *pdev)
{
    int ret = 0,i = 0;
    struct pinctrl_setting *setting = NULL;
    struct device_node *np = NULL;
    const char *statename = NULL;
    struct dev_pin_info *pins = NULL;

    if (pdev->name != NULL) {
        pr_info("platform device name %s",pdev->name);
    }

    /* 获取设备引脚状态信息 */
    /* 根节点下含有 compatile 属性的子节点,被转换为 platform_device
     * platform_device.dev.of_node指向device_node
     */
    /* 获取platform_device的device_node */
    np = pdev->dev.of_node;
    /* 从多个字符串属性中查找并读取字符串 */
    ret = of_property_read_string_index(np, "pinctrl-names", 0, &statename); // 读取pinctrl-names属性第0个值
    if (ret != 0) {
        dev_err(&pdev->dev, "of_property_read_string_index failed\n");
        return ret;
    }

    /* 1.获取与设备相关联的pinctrl句柄 */
    pins = pdev->dev.pins;
    myled.pinctrl = pins->p;
    if (IS_ERR(myled.pinctrl)) {
        dev_err(&pdev->dev, "retrieves the pinctrl handle for a device failed\n");
        ret = PTR_ERR(myled.pinctrl);
        goto faile_devid;
    } else {
        pr_info("retrieves the pinctrl handle for a device success\n");
    }

    /* 2. 获取指定的name的state */
    myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
    if (IS_ERR(myled.state)) {
        dev_err(&pdev->dev, "pinctrl lookup state failed\n");
        ret = PTR_ERR(myled.state);
        goto faile_devid;
    } else {
        pr_info("pinctrl lookup state success\n");
    }

    /*3、设置引脚状态 */
    ret = pinctrl_select_state(myled.pinctrl, myled.state);
    if (ret < 0) {
        dev_err(&pdev->dev, "pinctrl select state failed\n");
        goto faile_devid;
    } else {
        pr_info("pinctrl select state success\n");
    }


    /* 遍历当前状态下的所有setting */
    list_for_each_entry(setting, &(myled.state->settings), node) {
        printk("setting type %d", setting->type);
        printk("setting dev_name %s", setting->dev_name);    // 设备名称为myled
        if(setting->type == PIN_MAP_TYPE_MUX_GROUP ){        // 引脚复用 枚举值为2
            printk("setting mux group %d", setting->data.mux.group);
            printk("setting mux func %d", setting->data.mux.func);
        } else {                                              // 配置引脚电气特性
            printk("--------------configs start--------");
            printk("setting configs group_or_pin %d", setting->data.configs.group_or_pin);
            for(i=0; i<setting->data.configs.num_configs; i++){
                printk("setting configs configs %ld", setting->data.configs.configs[i]);
            }
            printk("--------------configs end--------");
        }
    }

    /* 4. 动态分配字符设备号 */
    ret = alloc_chrdev_region(&myled.devid, 0, 1, DTSLED_NAME);
    /* 返回值为负数，表示操作失败 */
    if (ret < 0) {
        dev_err(&pdev->dev, "alloc char device region failed\n");
        goto faile_devid;
    } else {
        pr_info("alloc char device region success\n");
    }

    /* 5.初始化字符设备，添加字符设备 */
    cdev_init(&myled.cdev, &led_fops);
    ret = cdev_add(&myled.cdev, myled.devid, DTSLED_CNT);
    /* 返回值为负数，表示操作失败 */
    if (ret < 0) {
        dev_err(&pdev->dev, "alloc char device region failed\n");
        goto fail_cdev;
    } else {
        pr_info("char device add success\n");
    }

    /* 6.创建类,它会在sys目录下创建/sys/class/dtsled这个类  */
    myled.class = class_create(THIS_MODULE, DTSLED_NAME);
    if (IS_ERR(myled.class)) {
        dev_err(&pdev->dev, "create class failed\n");
        ret = PTR_ERR(myled.class);
        goto fail_class;
    } else {
        pr_info("create class success\n");
    }

    /* 7. 在/sys/class/led下创建dtsled设备，然后mdev通过这个自动创建/dev/dtsled这个设备节点 */
    myled.device = device_create(myled.class, NULL, myled.devid, NULL, DTSLED_NAME);
    if (IS_ERR(myled.device)) {
         dev_err(&pdev->dev, "create device failed\n");
         ret = PTR_ERR(myled.device);
         goto fail_device;
    } else {
        pr_info("create device success\n");
    }
    
    return 0;
    
fail_findnd:
    device_destroy(myled.class, myled.devid);
fail_device:
    class_destroy(myled.class);
fail_class:
    cdev_del(&myled.cdev);
fail_cdev:
    unregister_chrdev_region(myled.devid, DTSLED_CNT);
faile_devid:
    return ret;
}

/*
 * 硬件信息被移除了，或者驱动被卸载了，全部要释放，释放资源的操作就放在该函数中
 */
static int led_remove(struct platform_device *pdev)
{
    /* 注销类、以及类设备 */
    device_destroy(myled.class, myled.devid);
    class_destroy(myled.class);

    /* 删除设备，卸载注册的设备编号 */
    cdev_del(&myled.cdev);
    unregister_chrdev_region(myled.devid, DTSLED_CNT);

    return 0;

}

/*
 * 用于设备树匹配
 */
static const struct of_device_id led_dt_match[] = {
    { .compatible = DTSLED_NAME, },
    {},
};

/*
 * platform驱动
 */
static struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,

    .driver = {
        .name = DTSLED_NAME,
        .of_match_table = led_dt_match,  // 匹配列表
    }
};


static int led_pinctrl_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&led_driver);
	if(ret < 0){
		printk("%s->%d platform_driver_register failed\n",__func__,__LINE__);
		return ret;
	} else {
        printk("%s->%d platform_driver_register successfully\n",__func__,__LINE__);
    }

    return 0;
}

/*
 * platform驱动模块出口
 */
static void led_pinctrl_exit(void)
{
    printk("platform driver unregistered\n");
    // platform驱动卸载
    platform_driver_unregister(&led_driver);
    return;
}

module_init(led_pinctrl_init);
module_exit(led_pinctrl_exit);

MODULE_AUTHOR("huang");
MODULE_DESCRIPTION("jz2440 pinctrl led driver");
MODULE_LICENSE("GPL");



